﻿using System;
using System.Collections.Generic;
using CatLib;
using CatLib.Container;
using Cysharp.Threading.Tasks;
using GameFramework.Core;
using GameFramework.Reflect;
using UnityEngine;
using Application = UnityEngine.Application;

namespace GameFramework.Localization
{
    /// <summary>
    /// 本地化服务实现
    /// </summary>
    internal sealed class LocalizationServiceImpl : IInitialize, IDisposable, ILocalizationService
    {
        [Inject]
        public IReflectService ReflectService { get; set; }
        
        [Inject]
        public IApplication Container { get; set; }

        public SystemLanguage CurrentLanguage { get; set; }
        public LocalizationStatus Status { get; private set; }

        public void Initialize()
        {
            CurrentLanguage = Application.systemLanguage; // TODO 从持久化服务中读取

            var baseType = typeof(ILocalization);

            ReflectService.ForEachType<ILocalization>(type =>
            {
                if (type.IsAbstract)
                    return;

                Type bindInterfaceType = null;
                var interfaces = type.GetInterfaces();
                foreach (var @interface in interfaces)
                {
                    if (@interface == baseType)
                        continue;

                    if (!baseType.IsAssignableFrom(@interface))
                        continue;

                    bindInterfaceType = @interface;
                }

                if (bindInterfaceType == null)
                    return;

                var instance = (ILocalization)Activator.CreateInstance(type);
                _localizations.Add(instance);

                Container.Instance(Container.Type2Service(bindInterfaceType), instance);
            });
        }

        public void Dispose()
        {
            Clean();
            
            foreach (var localization in _localizations)
                Container.Release(localization);
            
            _localizations.Clear();
        }

        public async UniTask<int> LoadAsync()
        {
            if (Status == LocalizationStatus.Loaded)
                return 0;

            if (Status == LocalizationStatus.Loading)
            {
                await UniTask.WaitWhile(() => Status == LocalizationStatus.Loading);

                return Status == LocalizationStatus.Loaded ? 0 : -1;
            }

            Status = LocalizationStatus.Loading;

            _buffer.Clear();
            foreach (var localization in _localizations)
                _buffer.Add(localization.LoadAsync(CurrentLanguage));

            await UniTask.WhenAll(_buffer);

            if (Status != LocalizationStatus.Loading)
                return -1;

            Status = LocalizationStatus.Loaded;
            return 0;
        }

        public void Clean()
        {
            Status = LocalizationStatus.Unload;

            foreach (var localization in _localizations)
                localization.Clean();
        }

        private readonly List<ILocalization> _localizations = new();
        private readonly List<UniTask> _buffer = new();
    }
}